#region

using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Vin.Extension.JwtBearer.Models;

#endregion

namespace Vin.Extension.JwtBearer;

public static class VinJwtExtension
{
    public const string OptionKey = "VinAuth:Jwt";

    public static JwtConfig? JwtConfig = new();

    public static JwtOption JwtOption = null!;

    public static void AddJwtAuthentication(this IServiceCollection serviceCollection, IConfiguration configuration)
    {
        JwtConfig ??= new JwtConfig();
        JwtOption? option = configuration.GetSection(OptionKey).Get<JwtOption>();
        if (option == null)
        {
            Console.WriteLine($"JwtOption配置错误，请检查app settings.json是否用{OptionKey}节点");
            return;
        }

        JwtOption = option;

        serviceCollection.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer((options) =>
            {
                //取出私钥
                var secretByte = Encoding.UTF8.GetBytes(option.SecretKey);

                // 配置token验证参数
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    LifetimeValidator = (_, expires, _, _) => DateTime.UtcNow < expires,
                    // LifetimeValidator = (notBefore, expires, _, _) => DateTime.UtcNow < expires && DateTime.UtcNow > notBefore,
                    //验证私钥
                    IssuerSigningKey = new SymmetricSecurityKey(secretByte),
                };

                // 额外配置
                JwtConfig.TriggerJwtBearerOptionsCallback(options);
            });
    }

    public static void AddClaimsToHttpContext(this HttpContext context, Dictionary<string, string>? claims)
    {
        if (claims is not { Count: > 0 }) return;

        ClaimsPrincipal currentUser = context.User;
        ClaimsIdentity identity = new ClaimsIdentity(currentUser.Identity);

        foreach (var (key, value) in claims)
        {
            if (identity.Claims.Any(m => m.Type != key))
            {
                identity.AddClaim(new Claim(key, value));
            }
        }

        ClaimsPrincipal newPrincipal = new ClaimsPrincipal(identity);
        context.User = newPrincipal;
    }

    public static void AddClaimsToHttpContext(this HttpContext context, string? key, string? value)
    {
        if (key is null || value is null) return;
        var claims = new Dictionary<string, string> { { key, value } };
        context.AddClaimsToHttpContext(claims);
    }

    public static void RemoveClaimsFromHttpContext(this HttpContext context, string key)
    {
        ClaimsPrincipal currentUser = context.User;
        ClaimsIdentity identity = new ClaimsIdentity(currentUser.Identity);
        var claim = identity.Claims.FirstOrDefault(m => m.Type == key);
        if (claim != null)
        {
            identity.RemoveClaim(claim);
        }

        ClaimsPrincipal newPrincipal = new ClaimsPrincipal(identity);
        context.User = newPrincipal;
    }
}